/*
 * sr_arg.c - EnCluster SR ASR(SR-ARG) Representation Parser
 * Clunix, yjlee@clunix.com, 2001.8.22,23.
 *
 * Copyright (c) 2001-2005 by Clunix Inc. All Rights Reserved.
 */

#include "config.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>

#include "sr.h"
#include "sr_p.h"

static ECM_INLINE void sprint_asr(char **pasr, const sr_t *sr);

static char	esctable[256] = {
	0, 1, 1, 1, 1, 1, 1, 'g', 'b', 't', 'r', 1, 'f', 'n', 1, 1,
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
	2, 2, '"', 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2,
	2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, '\\', 2, 2, 0,
	2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1,
	0,
};

static ECM_INLINE int
get_unesc_len(const char *str)
{
	register const char	*p = str;
	while (*p) {
		if (esctable[(unsigned char)*p] != 0)
			return -1;
		p++;
	}
	return (int)(p - str);
}

static ECM_INLINE ecm_size_t
sizeof_escape_string(const char *str)
{
	register const char	*p = str;
	int	size;

	if (str == NULL)
		return 0;

	if ((size = get_unesc_len(str)) >= 0)
		return size;

	size = 2;
	while (*p) {
		switch (esctable[(unsigned char)*p]) {
		case 0:
		case 2:
			size++;
			break;
		case 1:
			size += 4;	// '\012'
			break;
		default:
			size += 2;	// '\n'
			break;
		}
		p++;
	}
	return (ecm_size_t)size;
}

static ECM_INLINE void
escape_string(char **pbuf, const char *str)
{
	register const char	*p = str;
	int	len;
	char	*buf = *pbuf;

	if (str == NULL)
		return;
	if ((len = get_unesc_len(str)) >= 0) {
		if (len > 0)
			memcpy(buf, str, len);
		*pbuf += len;
		return;
	}

	*buf++ = '"';
	while (*p) {
		switch (esctable[(unsigned char)*p]) {
		case 0:
		case 2:
			*buf++ = *p++;
			break;
		case 1:
			buf += sprintf(buf, "\\%03o", *p);
			p++;
			break;
		default:
			*buf++ = '\\';
			*buf++ = *p++;
			break;
		}
	}
	*buf++ = '"';
	*pbuf = buf;
}

static ECM_INLINE ecm_size_t
sizeof_asrn(const char *name)
{
	return sizeof_escape_string(name);
}

static ECM_INLINE ecm_size_t
sizeof_asrt(srt_t srt)
{
	return srt_get_namelen(srt) + 2;		/* <typename> */
}

static ECM_INLINE ecm_size_t
sizeof_asrv(srt_t srt, sr_val_t srv)
{
	sr_printer_t	printer;
	ecm_size_t	buflen = 0, asrv_len;
	char		*buf;

	printer = srt_get_printer(srt);
	if (printer == NULL)
		return 0;
	buflen = 0;
	printer(srt, NULL, &buflen, srv);
	buf = ecm_malloc(buflen + 1);
	printer(srt, buf, &buflen, srv);
	buf[buflen] = '\0';

	asrv_len = sizeof_escape_string(buf);
	ecm_free(buf);
	return asrv_len;
}

static ECM_INLINE ecm_size_t
get_dir_asrvlen(const sr_t *sr_dir)
{
	sr_t	*sr_child;
	ecm_size_t	size = 2;	/* '{' and '}' */
	struct list_head	*lp;

	list_for_each(lp, &sr_dir->srv_head) {
		sr_child = list_entry(lp, sr_t, list);
		size += sr_asr_sizeof(sr_child);
		size++;		/* Semicolon */
	}
	return size;
}

static int
srt_dir_print(char *buf, ecm_size_t *pbuflen, const sr_t *sr_dir)
{
	ecm_size_t	size;
	struct list_head	*lp;
	sr_t	*sr_child;
	char	*p;

	size = get_dir_asrvlen(sr_dir);
	if (*pbuflen < size) {
		*pbuflen = size;
		return -1;
	}
	p = buf;
	*p++ = '{';

	list_for_each(lp, &sr_dir->srv_head) {
		sr_child = list_entry(lp, sr_t, list);
		p += sr_asr_sprint(p, sr_child);
		*p++ = ';';
	}
	*p++ = '}';
	return size;
}

static ECM_INLINE void
set_asrv(char **pasrv, srt_t srt, sr_val_t srv)
{
	char	*buf;

	sr_asrv_asprint(&buf, srt, srv);
	escape_string(pasrv, buf);
	ecm_free(buf);
}

static ECM_INLINE void
set_group_asrv(char **pasrv, srt_t srt, const sr_t *sr)
{
	sr_printer_t	printer;

	printer = srt_get_printer(srt);
	if (printer == NULL) {
		ecm_size_t	buflen = ECM_SIZE_MAX;
		*pasrv += srt_dir_print(*pasrv, &buflen, sr);
	}
	else
		set_asrv(pasrv, srt, sr_val_dir(sr));
}

static ECM_INLINE ecm_size_t
sizeof_dir_asrv(srt_t srt, const sr_t *sr_dir)
{
	sr_printer_t	printer;

	printer = srt_get_printer(srt);
	if (printer == NULL)
		return get_dir_asrvlen(sr_dir);

	return sizeof_asrv(srt, sr_val_dir(sr_dir));
}

ecm_size_t
sizeof_asr(const sr_t *sr)
{
	ecm_size_t	size;

	if (sr == NULL)
		return 0;	/* TODO: */
	size = sizeof_asrn(sr->name);
	size += sizeof_asrt(sr->type);
	if (sr_is_group(sr))
		size += sizeof_dir_asrv(sr->type, sr);
	else
		size += sizeof_asrv(sr->type, sr->srv_val);
	if (sr->sr_ext) {
		size += sizeof_asr(sr->sr_ext);
 		size += 4; /* {{}} */
	}
	return size;
}

static ECM_INLINE void
sprint_asrv(char **pasrv, srt_t srt, sr_val_t srv)
{
	sr_printer_t	printer;
	int		len;
	ecm_size_t	buflen = ECM_SIZE_MAX;

	printer = srt_get_printer(srt);
	if (printer == NULL) {
		if (srt_is_group(srt))
			set_group_asrv(pasrv, srt, srv.dir);
		return;
	}
	if ((len = printer(srt, *pasrv, &buflen, srv)) >= 0)
		(*pasrv) += len;
}

static ECM_INLINE void
set_asrn(char **pasrv, const char *name)
{
	escape_string(pasrv, name);
}

static ECM_INLINE void
set_asrt(char **pasrv, srt_t srt)
{
	char	*buf = *pasrv;

	buf += sprintf(buf, "<%s>", srt_get_name(srt));
	*pasrv = buf;
}

static ECM_INLINE void
set_ASRE(char **pasrv, sr_t *sr_ext)
{
	char	*buf;

	buf = *pasrv;
	*buf++ = '{';
	*buf++ = '{';
	sprint_asr(&buf, sr_ext);
	*buf++ = '}';
	*buf++ = '}';
	*pasrv = buf;
}

ecm_size_t
sr_asr_sizeof(const sr_t *sr)
{
	return sizeof_asr(sr);
}

ECMBOOL
sr_search_srt(const char *srtstr, srt_t *psrt)
{
	if (srtstr == NULL)
		return ECM_FALSE;
	if (!search_srt(srtstr, psrt))
		return ECM_FALSE;
	return ECM_TRUE;
}

ecm_size_t
sr_asrv_sizeof(srt_t srt, sr_val_t srv)
{
	return sizeof_asrv(srt, srv);
}

void
asr_input(yyscan_t scanner, char *buf, int *presult, int max_size)
{
	int	copycnt;
	asr_input_t	*input = srpget_extra(scanner);
	
	if (input->instr_ptr < 0) {
		*presult = 0;
		return;
	}
	if (input->instr_ptr == input->instr_len) {
		buf[0] = '\n';
		*presult = 1;
		input->instr_ptr = -1;
		return;
	}
	copycnt = input->instr_len - input->instr_ptr;
	if (copycnt > max_size)
		copycnt = max_size;
	memcpy(buf, input->instr + input->instr_ptr, copycnt);
	input->instr_ptr += copycnt;
	*presult = copycnt;
}

sr_t *
asr_create_ASRT(char *name, ECMBOOL is_HMS, ECMBOOL is_ref)
{
	sr_t	*sr = NULL;
	srt_t	srt;
	char	*srtstr;

	ecm_asprintf(&srtstr, "%s%s%s",
		     is_ref ? "&": "",
		     name,
		     is_HMS ? "*": "");
	if (sr_search_srt(srtstr, &srt))
		sr = sr_create(srt);
	ecm_free(name);
	return sr;
}

sr_t *
asr_create_ASR(char *name, sr_t *sr_srt, sr_t *sr_asrv, sr_t *sr_ext)
{
	sr_t		*sr;

	if (sr_srt == NULL) {
		ecm_free(name);
		sr_free(sr_asrv);
		sr_free(sr_ext);
		return NULL;
	}
	sr = sr_srt;
	sr_set_name(sr, name);
	ecm_free(name);

	if (sr_asrv) {
		if (sr_is_HMS(sr)) {
			sr_merge_force(sr, sr_asrv);
			sr_free(sr_asrv);
		}
		else if (sr_is_group(sr_asrv)) {
			sr_asrv->type = sr->type;
			sr_set_val(sr, sr_val_dir(sr_asrv));
		}
		else {
			sr_val_t	val;

			if (!sr_casrv_scan(sr_get_str(sr_asrv), sr_get_type(sr_srt), &val)) {
				sr_free(sr_asrv);
				sr_free(sr_ext);
				sr_free(sr);
				return NULL;
			}
			sr_set_val(sr, val);
			sr_free(sr_asrv);
		}
	}

	sr->sr_ext = sr_ext;
	return sr;
}

ECM_INLINE sr_t *
scan_asr(char *asr)
{
	sr_t		*sr = NULL;
	asr_input_t	input;
	yyscan_t	scanner;

	if (asr == NULL)
		return NULL;

	srplex_init(&scanner);
	srpset_extra(&input, scanner);

	SR_DEBUG_FLEX(scanner);

	input.instr = asr;
	input.instr_len = ecm_strlen(asr);
	input.instr_ptr = 0;

	srpparse(scanner, &sr);
	srplex_destroy(scanner);

	return sr;
}

static ECM_INLINE void
sprint_asr(char **pasr, const sr_t *sr)
{
	char	*buf;

	if (sr == NULL)
		return;

	buf = *pasr;

	set_asrn(&buf, sr->name);
	set_asrt(&buf, sr->type);
	if (sr_is_group(sr))
		set_group_asrv(&buf, sr->type, sr);
	else
		set_asrv(&buf, sr->type, sr->srv_val);
	if (sr->sr_ext)
		set_ASRE(&buf, sr->sr_ext);
	*pasr = buf;
}

int
sr_asr_asprint(char **pbuf, const sr_t *sr)
{
	char	*asr;
	ecm_size_t	size;

	if (pbuf == NULL || sr == NULL)
		return -1;
	size = sizeof_asr(sr);
	asr = (char *)ecm_malloc(size + 1);
	*pbuf = asr;
	sprint_asr(&asr, sr);
	*asr = '\0';
	return size;
}

int
sr_asr_sprint(char *buf, const sr_t *sr)
{
	char	*p;

	if (buf == NULL || sr == NULL)
		return -1;
	p = buf;
	sprint_asr(&p, sr);
	*p = '\0';
	return (int)(p - buf);
}

/* returns NULL on error */
sr_t *
sr_asr_scan(char *asr)
{
	return scan_asr(asr);
}

sr_t *
sr_casr_scan(const char *casr)
{
	sr_t	*sr;
	char	*asr;

	asr = ecm_strdup(casr);
	sr = scan_asr(asr);
	ecm_free(asr);
	return sr;
}

char *
sr_get_asrv(const sr_t *sr)
{
	char	*asrv;

	if (sr == NULL)
		return NULL;
	sr_asrv_asprint(&asrv, sr->type, sr_get_val(sr));
	return asrv;
}

int
sr_asrv_sprint(char *buf, srt_t srt, sr_val_t srv)
{
	char	*p;

	p = buf;
	sprint_asrv(&p, srt, srv);
	*p = '\0';
	return (int)(p - buf);
}

int
sr_asrv_asprint(char **pbuf, srt_t srt, sr_val_t srv)
{
	char	*asrv;
	ecm_size_t	size;

	if (pbuf == NULL)
		return -1;
	size = sizeof_asrv(srt, srv);
	asrv = (char *)ecm_malloc(size + 1);
	*pbuf = asrv;
	sprint_asrv(&asrv, srt, srv);
	*asrv = '\0';
	return size;
}

ECMBOOL
sr_asrv_scan(char *asrv, srt_t srt, sr_val_t *psrv)
{
	sr_scanner_t	scanner;

	if (psrv == NULL || asrv == NULL)
		return ECM_FALSE;

	scanner = srt_get_scanner(srt);
	if (!scanner(srt, asrv, psrv))
		return ECM_FALSE;
	return ECM_TRUE;
}

ECMBOOL
sr_casrv_scan(const char *c_asrv, srt_t srt, sr_val_t *psrv)
{
	char	*asrv;
	ECMBOOL	res;

	asrv = ecm_strdup(c_asrv);
	res = sr_asrv_scan(asrv, srt, psrv);
	ecm_free(asrv);
	return res;
}

ECMBOOL
sr_casri_scan(const char *c_asrv, srt_t srt, ecm_int32 *pival)
{
	sr_val_t	val;

	if (!sr_casrv_scan(c_asrv, srt, &val))
		return ECM_FALSE;
	*pival = val.ival;
	return ECM_TRUE;
}
